import { ArrayMethods } from './array';
import Dep from './dep';

//a.xxx = 444 && objList[0].xxx = 555， xxx 劫持不到，要用 $set

// 传入的 data:{
//   message: "bbbb",
//   a: { b: 1 },
//   list: [1, 2, 3],
//   objList: [{ x: 1, y: 2 }],
// }

export function observer(data) {
  if (typeof data !== 'object' || data === null) {
    return;
  }
  if (data.__ob__ instanceof Observer) {
    return data.__ob__;
  }
  return new Observer(data);
}

class Observer {
  constructor(data) {
    this.dep = new Dep();

    // 添加 __ob__ 属性，值为 Observer 实例。作用 1： 劫持 array 新增的数据时可以从这个属性获取 Observer 类，从而调用实例的 observerArray 方法；作用 2： 作为数据是否被劫持过的标识。
    Object.defineProperty(data, '__ob__', {
      enumerable: false, // 不可枚举，walk 循环时不获取，否则陷入死循环
      value: this,
    });

    // 数组和对象调用不同的方法
    if (Array.isArray(data)) {
      data.__proto__ = ArrayMethods;
      this.observerArray(data);
    } else {
      this.walk(data);
    }
  }

  walk(data) {
    Object.keys(data).forEach((key) => defineReactive(data, key, data[key]));
  }

  observerArray(data) {
    data.forEach((item) => observer(item));
  }
}

function dependArray(value) {
  for (let i = 0; i < value.length; i++) {
    const current = value[i];
    current.__ob__ && current.__ob__.dep.depend();
    if (Array.isArray(current)) {
      dependArray(current);
    }
  }
}

function defineReactive(target, key, value) {
  let childOb = observer(value); // 深度递归劫持，如 a:{b:1}
  let dep = new Dep();
  Object.defineProperty(target, key, {
    get() {
      if (Dep.target) {
        dep.depend();
        if (childOb) {
          childOb.dep.depend();

          // 处理数组套数组
          if (Array.isArray(value)) {
            dependArray(value);
          }
        }
      }
      return value;
    },
    set(newValue) {
      // console.log('set', key, newValue);
      if (newValue === value) return;
      observer(newValue); // a:{b:1} => a:{c:2} 对 c 进行劫持
      value = newValue;
      dep.notify();
    },
  });
}
